﻿using Gasgoo.ShareLibrary.Framework.iDataProvider;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Threading.Tasks;


public enum OrderByEnum
{
    Asc, Desc
}
public class PageOptions
{
    public int PageNumber { get; set; }
    public int PageSize { get; set; }
}
public class Queryable<T> where T : AbstractDataEntity
{
    public Queryable(T Value)
    {
        this.Value = Value;
    }
    public T Value { get; }
    public List<Expression<Func<T, bool>>>? Where { get; set; }
    public List<Expression<Func<T, object>>>? GroupBy { get; set; }
    public List<KeyValuePair<OrderByEnum, Expression<Func<T, object>>>>? OrderBy { get; set; }
    public PageOptions? Page { get; set; }

}

public static class DataEntityExtensions
{
    // where
    public static Queryable<T> Where<T>(this T entity, Expression<Func<T, bool>> expression) where T : AbstractDataEntity
    {
        return new Queryable<T>(entity)
        {
            Where = new List<Expression<Func<T, bool>>> { expression }
        };
    }

    public static Queryable<T> WhereIF<T>(this T entity, bool isWhere, Expression<Func<T, bool>> expression) where T : AbstractDataEntity
    {
        return isWhere ? Where(entity, expression) : new Queryable<T>(entity);
    }

    // group by
    public static Queryable<T> GroupBy<T>(this T entity, Expression<Func<T, object>> expression) where T : AbstractDataEntity
    {
        return new Queryable<T>(entity)
        {
            GroupBy = new List<Expression<Func<T, object>>> { expression }
        };
    }

    public static Queryable<T> GroupByIF<T>(this T entity, bool isGroupBy, Expression<Func<T, object>> expression) where T : AbstractDataEntity
    {
        return isGroupBy ? GroupBy(entity, expression) : new Queryable<T>(entity);
    }

    // order by
    public static Queryable<T> OrderBy<T>(this T entity, Expression<Func<T, object>> expression) where T : AbstractDataEntity
    {
        return new Queryable<T>(entity)
        {
            OrderBy = new List<KeyValuePair<OrderByEnum, Expression<Func<T, object>>>>
                {
                    new KeyValuePair<OrderByEnum, Expression<Func<T, object>>>(OrderByEnum.Asc, expression)
                }
        };
    }
    public static Queryable<T> OrderByIF<T>(this T entity, bool isOrderBy, Expression<Func<T, object>> expression) where T : AbstractDataEntity
    {
        return isOrderBy ? OrderBy(entity, expression) : new Queryable<T>(entity);
    }
    public static Queryable<T> OrderByDescending<T>(this T entity, Expression<Func<T, object>> expression) where T : AbstractDataEntity
    {
        return new Queryable<T>(entity)
        {
            OrderBy = new List<KeyValuePair<OrderByEnum, Expression<Func<T, object>>>>
                {
                    new KeyValuePair<OrderByEnum, Expression<Func<T, object>>>(OrderByEnum.Desc, expression)
                }
        };
    }
    public static Queryable<T> OrderByDescendingIF<T>(this T entity, bool isOrderBy, Expression<Func<T, object>> expression) where T : AbstractDataEntity
    {
        return isOrderBy ? OrderByDescending(entity, expression) : new Queryable<T>(entity);
    }

    // get result
    public static Task<List<T>> ToPageListAsync<T>(this T entity, int pageNumber, int pageSize) where T : AbstractDataEntity
    {
        var query = new Queryable<T>(entity);
        query.Page = new PageOptions
        {
            PageNumber = pageNumber,
            PageSize = pageSize
        };
        return entity.GetDataStorageProvider().QueryableAsync(query);
    }

    public static Task<List<T>> ToListAsync<T>(this T entity) where T : AbstractDataEntity
    {
        var query = new Queryable<T>(entity);
        return entity.GetDataStorageProvider().QueryableAsync(query);
    }


    // where
    public static Queryable<T> Where<T>(this Queryable<T> queryable, Expression<Func<T, bool>> expression) where T : AbstractDataEntity
    {
        queryable.Where = queryable.Where ?? new List<Expression<Func<T, bool>>>();
        queryable.Where!.Add(expression);
        return queryable;
    }
    public static Queryable<T> WhereIF<T>(this Queryable<T> queryable, bool isWhere, Expression<Func<T, bool>> expression) where T : AbstractDataEntity
    {
        if (isWhere)
            queryable.Where(expression);
        return queryable;
    }

    // group by
    public static Queryable<T> GroupBy<T>(this Queryable<T> queryable, Expression<Func<T, object>> expression) where T : AbstractDataEntity
    {
        queryable.GroupBy = queryable.GroupBy ?? new List<Expression<Func<T, object>>>();
        queryable.GroupBy.Add(expression);
        return queryable;
    }
    public static Queryable<T> GroupByIF<T>(this Queryable<T> queryable, bool isGroupBy, Expression<Func<T, object>> expression) where T : AbstractDataEntity
    {
        if (isGroupBy)
            queryable.GroupBy(expression);
        return queryable;
    }

    // order by
    public static Queryable<T> OrderBy<T>(this Queryable<T> queryable, Expression<Func<T, object>> expression) where T : AbstractDataEntity
    {
        queryable.OrderBy = queryable.OrderBy ?? new List<KeyValuePair<OrderByEnum, Expression<Func<T, object>>>>();
        queryable.OrderBy(expression);
        return queryable;
    }
    public static Queryable<T> OrderByIF<T>(this Queryable<T> queryable, bool isOrderBy, Expression<Func<T, object>> expression) where T : AbstractDataEntity
    {
        if (isOrderBy)
            queryable.OrderBy(expression);
        return queryable;
    }
    public static Queryable<T> OrderByDescending<T>(this Queryable<T> queryable, Expression<Func<T, object>> expression) where T : AbstractDataEntity
    {
        queryable.OrderBy = queryable.OrderBy ?? new List<KeyValuePair<OrderByEnum, Expression<Func<T, object>>>>();
        queryable.OrderBy.Add(new KeyValuePair<OrderByEnum, Expression<Func<T, object>>>(OrderByEnum.Desc, expression));
        return queryable;
    }
    public static Queryable<T> OrderByDescendingIF<T>(this Queryable<T> queryable, bool isOrderBy, Expression<Func<T, object>> expression) where T : AbstractDataEntity
    {
        if (isOrderBy)
            queryable.OrderByDescending(expression);
        return queryable;
    }

    // get result
    public static Task<List<T>> ToPageListAsync<T>(this Queryable<T> queryable, int pageNumber, int pageSize) where T : AbstractDataEntity
    {
        queryable.Page = new PageOptions
        {
            PageNumber = pageNumber,
            PageSize = pageSize
        };
        return queryable.Value.GetDataStorageProvider().QueryableAsync(queryable);
    }
    public static Task<List<T>> ToListAsync<T>(this Queryable<T> queryable) where T : AbstractDataEntity
    {
        return queryable.Value.GetDataStorageProvider().QueryableAsync(queryable);
    }
}
